# bot_api_v1/app/tasks/celery_tiktok_service.py (已修正路径计算)
import logging
import asyncio
import time
import os
import sys
from pathlib import Path
from typing import Dict, Any, Optional,List, Tuple
from concurrent.futures import ThreadPoolExecutor
import tempfile

# 导入所需的原 TikTokService 中的依赖和错误类
try:
    # 尝试从原始路径导入，因为这个新文件在 tasks 下
    from bot_api_v1.app.services.business.tiktok_service import (
        TikTokError, InitializationError, VideoFetchError, UserFetchError, 
        AudioDownloadError, AudioTranscriptionError, MediaType,UserPostsFetchError,PlatformError
    )
    from bot_api_v1.app.services.business.script_service import ScriptService
except ImportError:
     # 如果路径或项目结构不同，需要调整
     # 例如: from ..services.business.tiktok_service import ...
     # 例如: from ..services.business.script_service import ScriptService
     logging.error("CeleryTikTokService: 无法导入依赖的服务或错误类，请检查路径！", exc_info=True)
     raise

from bot_api_v1.app.core.logger import logger

# --- 同步执行异步代码的辅助函数 ---
def run_async_in_new_loop(coro):
    try:
        # 尝试获取或设置事件循环 (适用于不同线程环境)
        try:
            loop = asyncio.get_event_loop_policy().get_event_loop()
        except RuntimeError:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
        
        # 检查循环是否正在运行 (通常在新线程/进程中不会)
        if loop.is_running():
             logger.warning("Detected running event loop in run_async_in_new_loop (Celery Task context). Using threadsafe execution.")
             # 在 Celery 环境下，如果检测到正在运行的循环，使用 run_coroutine_threadsafe 可能更安全
             # 但这需要调用者能访问那个循环，比较复杂。
             # 直接运行 asyncio.run 通常在新线程中是安全的。
             # 为了简单，我们先坚持 asyncio.run
             return asyncio.run(coro) # 在新线程中，asyncio.run 会创建新循环
        else:
             return asyncio.run(coro)
             
    except Exception as e:
        logger.error(f"run_async_in_new_loop failed: {e}", exc_info=True)
        raise

class CeleryTikTokService:
    """专门为 Celery Task 设计的、执行 TikTok 相关操作的同步服务类"""
    DEFAULT_TIMEOUT = 30
    MAX_RETRIES = 3

    # --- 请用这段代码替换你的 __init__ 方法 ---
    def __init__(self, cookie: Optional[str] = None, timeout: int = DEFAULT_TIMEOUT, max_retries: int = MAX_RETRIES):
        try:
             from bot_api_v1.app.services.business.script_service import ScriptService 
             self.script_service = ScriptService() 
        except ImportError:
             logger.error("CeleryTikTokService: 无法导入 ScriptService!", exc_info=True)
             raise InitializationError("缺少 ScriptService 依赖")
             
        # self._imports = {}
        # self._original_dir = os.getcwd()
        # self.parameters = None
        # self.console = None
        # self.settings_obj = None 
        # self.cookie_object = None
        self.cookie = cookie
        self.timeout = timeout
        self.max_retries = max_retries

        
        # 获取项目根目录路径
        current_dir = Path(__file__).resolve().parent
        project_root = current_dir.parents[3]  # bot-api-v1 项目根目录
        self.tiktok_lib_path = project_root / "src" / "bot_api_v1" / "libs" / "tiktok_downloader"
        
        # These will be initialized in __aenter__
        self.console = None
        self.settings = None
        self.cookie_object = None
        self.parameters = None

        self.script_service = ScriptService()

        class DummyRecorder:
            def __init__(self):
                self.field_keys = []
            
            async def save(self, *args, **kwargs):
                pass
            
        self.DummyRecorder = DummyRecorder
        
        # Import required modules only when actually needed
        # self._setup_imports()
        # 执行初始化，包含修正后的路径计算
        try:        
             self._setup_imports() # 这里的执行依赖于上面的路径计算正确
             self._initialize_sync() # 这里的执行依赖于 _setup_imports 成功
             logger.info("CeleryTikTokService 初始化完成。")
             
        except Exception as e:
             # 捕获包括上面提前抛出的 InitializationError 在内的所有初始化错误
             logger.error(f"CeleryTikTokService 初始化失败: {e}", exc_info=True)
             # 统一向上抛出 InitializationError
             raise InitializationError(f"CeleryTikTokService 初始化失败: {e}") from e
    # --- 结束替换区域 ---


    def _setup_imports(self) -> None:
        # --- 复制原 TikTokService 的 _setup_imports 逻辑 ---
        # (你需要确保这里的代码是你TikTokService中实际工作的代码)
        try:
            # 使用配置中提供的子模块路径
            tiktok_root = Path(self.tiktok_lib_path) # 子模块根目录

            # 检查子模块路径是否存在
            if not tiktok_root.is_dir():
                logger.error(f"TikTok downloader path not found: {tiktok_root}")
                # 可能是子模块未初始化，提示用户
                raise ImportError(f"TikTok downloader library not found at {tiktok_root}. Did you run 'git submodule update --init --recursive'?")

            # --- 新策略：将子模块的根目录添加到 Python 搜索路径 (sys.path) ---
            if str(tiktok_root) not in sys.path:
                sys.path.insert(0, str(tiktok_root))
                logger.debug(f"Added submodule root {tiktok_root} to Python path")

            # --- 处理工作目录更改 (存在风险，但与新 sys.path 策略一致) ---
            self._original_dir = os.getcwd()
            # 警告：os.chdir 更改全局状态，在并发或库代码中存在风险。
            # 只有在确认 tiktok_downloader 库严格要求在其根目录运行时才应保留。
            if os.getcwd() != str(tiktok_root):
                os.chdir(str(tiktok_root))
                logger.debug(f"Changed working directory to {tiktok_root} (Required by submodule?)")
            # --- 工作目录更改结束 ---

            # 从子模块的 src 目录导入所需组件
            from src.config import Settings, Parameter
            from src.custom import PROJECT_ROOT # 这是子模块内部的 PROJECT_ROOT
            from src.tools import ColorfulConsole
            from src.module import Cookie
            #抖音接口
            from src.interface import Detail, User, Comment, Account, Search 
            #TikTok接口
            from src.interface.detail_tiktok import DetailTikTok 
            from src.interface.info_tiktok import InfoTikTok 
            from src.interface.comment_tiktok import CommentTikTok
            from src.interface.account_tiktok import AccountTikTok
            #通用提取器
            from src.link import Extractor, ExtractorTikTok
            from src.extract import Extractor as DataExtractor 
            from src.record import BaseLogger
            #搜索模型
            from src.module.search_model import ( 
                GeneralSearch,
                VideoSearch,
                UserSearch,
                LiveSearch,
                BaseSearch 
            )

            self._imports = {
                "Settings": Settings,
                "Parameter": Parameter,
                "PROJECT_ROOT": PROJECT_ROOT, 
                "ColorfulConsole": ColorfulConsole,
                "Cookie": Cookie,
                "Detail": Detail, 
                "DetailTikTok": DetailTikTok, 
                "User": User, 
                "InfoTikTok": InfoTikTok, 
                "Comment": Comment,
                "CommentTikTok": CommentTikTok,
                "Account": Account, # 抖音账号作品
                "AccountTikTok": AccountTikTok, # TikTok账号作品
                "Extractor": Extractor,
                "ExtractorTikTok": ExtractorTikTok,
                "DataExtractor": DataExtractor,
                "BaseLogger": BaseLogger,
                "Search": Search,
                "GeneralSearch": GeneralSearch,
                "VideoSearch": VideoSearch,
                "UserSearch": UserSearch,
                "LiveSearch": LiveSearch,
                "BaseSearch": BaseSearch, 
            }
            
            logger.debug("CeleryTikTokService: Successfully imported TikTok downloader modules.")

        except Exception as e:
            logger.error(f"CeleryTikTokService: Failed to import TikTok modules: {e}", exc_info=True)
            raise ImportError(f"CeleryTikTokService: Could not import TikTok modules: {e}") from e
        # finally:
        #      # 确保恢复 CWD (如果切换了)
        #      if changed_cwd and os.getcwd() != previous_cwd:
        #          try:
        #              os.chdir(previous_cwd)
        #              logger.debug(f"Restored working directory to {previous_cwd}")
        #          except Exception as chdir_e:
        #              logger.error(f"Failed to restore working directory from _setup_imports: {chdir_e}")


    def _initialize_sync(self) -> None:
        """执行同步初始化，类似原 __enter__"""
        # --- 复制原 __enter__ 的核心逻辑 ---
        # (代码同你上次提供的，看起来没问题)
        try:
            self.console = self._imports["ColorfulConsole"]()
            self.settings = self._imports["Settings"](
                self._imports["PROJECT_ROOT"], 
                self.console
            )
            self.cookie_object = self._imports["Cookie"](
                self.settings, 
                self.console
            )
            
            # Get settings data
            settings_data = self.settings.read()
            
            # Update with provided cookie if available
            if self.cookie:
                try:
                    cookie_dict = self.cookie_object.extract(
                        self.cookie, 
                        write=False
                    )
                    settings_data["cookie"] = cookie_dict
                    logger.debug("Updated settings with provided cookie")
                except Exception as e:
                    logger.warning(f"Failed to extract cookie: {str(e)}")
            
            # Override timeout setting
            settings_data["timeout"] = self.timeout
            
            # Initialize parameters
            self.parameters = self._imports["Parameter"](
                self.settings,
                self.cookie_object,
                logger=self._imports["BaseLogger"],
                console=self.console,
                recorder=None,  # No recorder needed
                **settings_data
            )
            
            # Set up headers and cookies
            self.parameters.set_headers_cookie()
            logger.debug("CeleryTikTokService: Synchronous initialization complete.")
        except Exception as e:
             logger.error(f"[Celery TikTok Init] Initialization failed: {e}", exc_info=True)
             raise InitializationError(f"Failed sync init: {e}") from e

    def close_sync(self):
        """同步清理资源，类似原 __exit__"""
        # --- 复制原 __exit__ 的核心逻辑 ---
        # (代码同你上次提供的，包含关闭 client 的尝试和 CWD 恢复)
        logger.debug("CeleryTikTokService: Entering synchronous cleanup (close_sync)")
        try:
            if self.parameters:
                try:
                    self.parameters.close_client()
                    logger.debug("Closed HTTP client")
                    
                    if hasattr(self, '_original_dir'):
                        os.chdir(self._original_dir)
                except Exception as close_e:
                     logger.error(f"[Celery TikTok Cleanup] Error closing HTTP client: {close_e}", exc_info=True)
            # CWD 恢复逻辑（如果 _setup_imports 中切换了目录）
            # if os.getcwd() != self._original_dir:
            #     os.chdir(self._original_dir)
            #     logger.debug(f"Restored working directory to {self._original_dir}")
        except Exception as e:
            logger.warning(f"Error during synchronous cleanup: {str(e)}", exc_info=True)


    def get_video_info_sync(
        self,
        url: str,
        extract_text: bool,
        user_id_for_points: str, 
        trace_id: str,
        root_trace_key: str
    ) -> dict:
        """
        [同步执行] 获取抖音视频信息，并可选提取文本。
        这是 CeleryTikTokService 的核心方法。
        """
        # --- 逻辑基本保持不变，依赖于正确的初始化和同步 ScriptService ---
        log_extra = {"request_id": trace_id, "user_id": user_id_for_points,"root_trace_key": root_trace_key}
        logger.info(f"[Celery TikTok Service] 开始获取视频信息: {url}, extract_text={extract_text}", extra=log_extra)

        points_consumed = 0
        base_cost = 10 
        transcription_cost = 0
        media_data = None
        transcribed_text = None

        if not self.parameters:
             logger.error("[Celery TikTok Service] 服务未初始化 (parameters is None)!", extra=log_extra)
             raise InitializationError("服务未初始化")

        try:
            # 1. 获取基础视频信息 (在线程池中运行异步库代码)
            async def _fetch_tiktok_video_async(inner_trace_id: str, inner_url: str):
                log_extra_inner = {"request_id": inner_trace_id}
                logger.debug("[Celery TikTok Service] ---> Entering _fetch_tiktok_video_async (in thread) <---", extra=log_extra_inner)
                # ... (内部调用 extractor, detail, data_extractor 的 await 逻辑) ...
                # ... (包含详细的 DEBUG 日志 和 精确的 TypeError 捕获) ...
                # --- Placeholder Start ---
                logger.debug(f"Self.parameters check: {bool(self.parameters)}")
                extractor = self._imports["Extractor"](self.parameters)
                logger.debug(f"[Async Fetch Inner] Extractor created: {extractor}")
                extractor_run_result = extractor.run(inner_url)
                logger.debug(f"[Async Fetch Inner] extractor.run returned type: {type(extractor_run_result)}")
                if extractor_run_result is None: raise VideoFetchError("Extractor returned None")
                video_ids = await extractor_run_result
                if not video_ids: raise VideoFetchError("No ID")
                video_id = video_ids[0]
                detail = self._imports["Detail"](self.parameters, detail_id=video_id)
                logger.debug(f"[Async Fetch Inner] Detail created: {detail}")
                detail_run_result = detail.run()
                logger.debug(f"[Async Fetch Inner] detail.run returned type: {type(detail_run_result)}")
                if detail_run_result is None: raise VideoFetchError("Detail returned None")
                video_data_raw = await detail_run_result
                if not video_data_raw: raise VideoFetchError("No Detail Data")
                data_extractor = self._imports["DataExtractor"](self.parameters)
                dummy_recorder = self.DummyRecorder()
                logger.debug(f"[Async Fetch Inner] DataExtractor created: {data_extractor}")
                data_extractor_run_result = data_extractor.run([video_data_raw], dummy_recorder, tiktok=False)
                logger.debug(f"[Async Fetch Inner] data_extractor.run returned type: {type(data_extractor_run_result)}")
                processed_data = None
                try: processed_data = await data_extractor_run_result
                except TypeError as te:
                    if "NoneType" in str(te): raise VideoFetchError(f"数据处理失败: {te}") from te
                    else: raise
                except Exception as await_exc: raise VideoFetchError(f"数据处理未知错误: {await_exc}") from await_exc
                if not processed_data: raise VideoFetchError("No Processed Data")
                return processed_data[0]
                # --- Placeholder End ---

            def _run_fetch_in_thread():
                try: return asyncio.run(_fetch_tiktok_video_async(trace_id, url))
                except Exception as thread_e: logger.error(f"[Celery TikTok Service Thread] Error: {thread_e}", exc_info=True, extra=log_extra); raise thread_e

            logger.debug("[Celery TikTok Service] 执行基础信息获取 (ThreadPool)...", extra=log_extra)
            with ThreadPoolExecutor(max_workers=1) as executor:
                future = executor.submit(_run_fetch_in_thread)
                try: media_data = future.result(timeout=self.timeout + 10)
                except TimeoutError: raise TikTokError(f"获取基础信息超时 (ThreadPool)")
                except Exception as e: raise TikTokError(f"获取基础信息失败: {e}") from e

            logger.debug("[Celery TikTok Service] 获取到基础信息", extra=log_extra)
            points_consumed = base_cost
            if media_data is None: raise TikTokError("未能获取基础信息")

            # 2. 提取文本
            if extract_text and isinstance(media_data, dict) and media_data.get("type") == MediaType.VIDEO:
                media_url = media_data.get("music_url") or media_data.get("downloads")
                if media_url:
                    logger.info(f"[Celery TikTok Service] 下载和转写...", extra=log_extra)
                    try:
                        audio_path, _ = self.script_service.download_audio_sync(media_url, trace_id)
                        transcribed_text, duration = self.script_service.transcribe_audio_sync(audio_path, trace_id)
                        if duration >= 0: t_cost = max(10, ((int(duration)//60)+(1 if int(duration)%60>0 else 0))*10)
                        else: t_cost = 10
                        transcription_cost = t_cost
                        logger.info(f"[Celery TikTok Service] 转写成功", extra=log_extra)
                    except (AudioDownloadError, AudioTranscriptionError) as e: transcribed_text = f"失败:{e}"; transcription_cost = 0
                    except Exception as e: transcribed_text = f"内部错误({trace_id})"; transcription_cost = 0
                else: transcribed_text = "无URL"; transcription_cost = 0
                if isinstance(media_data, dict): media_data["transcribed_text"] = transcribed_text

            points_consumed += transcription_cost
            logger.info(f"[Celery TikTok Service] 处理成功: {url}", extra=log_extra)
            return {"status": "success", "data": media_data, "points_consumed": points_consumed}

        # 保持错误处理不变
        except (TikTokError, InitializationError, AudioDownloadError, AudioTranscriptionError, VideoFetchError, UserFetchError) as e:
             error_msg = f"处理失败 ({type(e).__name__}): {str(e)}"
             logger.error(f"[Celery TikTok Service] {error_msg}", extra=log_extra, exc_info=False)
             return {"status": "failed", "error": error_msg, "points_consumed": 0}
        except Exception as e:
             error_msg = f"发生意外错误: {str(e)}"
             logger.error(f"[Celery TikTok Service] {error_msg}", exc_info=True, extra=log_extra)
             return {"status": "failed", "error": f"发生内部错误 ({trace_id})", "exception": str(e), "points_consumed": 0}
        finally:
             self.close_sync() # 确保调用清理

    # ... (可能需要的 get_user_info_sync 方法) ...

    def get_basic_video_info_sync_internal(
        self,
        url: str,
        extract_text: bool,
        user_id_for_points: str, 
        trace_id: str
    ) -> dict:
        """
        [同步执行] 获取抖音视频信息，并可选提取文本。
        这是 CeleryTikTokService 的核心方法。
        """
        # --- 逻辑基本保持不变，依赖于正确的初始化和同步 ScriptService ---
        log_extra = {"request_id": trace_id, "user_id": user_id_for_points}
        logger.info(f"[Celery TikTok Service] 开始获取视频信息: {url}, extract_text={extract_text}", extra=log_extra)

        points_consumed = 0
        base_cost = 10 
        transcription_cost = 0
        media_data = None
        transcribed_text = None

        if not self.parameters:
             logger.error("[Celery TikTok Service] 服务未初始化 (parameters is None)!", extra=log_extra)
             raise InitializationError("服务未初始化")

        try:
            # 1. 获取基础视频信息 (在线程池中运行异步库代码)
            async def _fetch_tiktok_video_async(inner_trace_id: str, inner_url: str):
                log_extra_inner = {"request_id": inner_trace_id}
                logger.debug("[Celery TikTok Service] ---> Entering _fetch_tiktok_video_async (in thread) <---", extra=log_extra_inner)
                # ... (内部调用 extractor, detail, data_extractor 的 await 逻辑) ...
                # ... (包含详细的 DEBUG 日志 和 精确的 TypeError 捕获) ...
                # --- Placeholder Start ---
                logger.debug(f"Self.parameters check: {bool(self.parameters)}")
                extractor = self._imports["Extractor"](self.parameters)
                logger.debug(f"[Async Fetch Inner] Extractor created: {extractor}")
                extractor_run_result = extractor.run(inner_url)
                logger.debug(f"[Async Fetch Inner] extractor.run returned type: {type(extractor_run_result)}")
                if extractor_run_result is None: raise VideoFetchError("Extractor returned None")
                video_ids = await extractor_run_result
                if not video_ids: raise VideoFetchError("No ID")
                video_id = video_ids[0]
                detail = self._imports["Detail"](self.parameters, detail_id=video_id)
                logger.debug(f"[Async Fetch Inner] Detail created: {detail}")
                detail_run_result = detail.run()
                logger.debug(f"[Async Fetch Inner] detail.run returned type: {type(detail_run_result)}")
                if detail_run_result is None: raise VideoFetchError("Detail returned None")
                video_data_raw = await detail_run_result
                if not video_data_raw: raise VideoFetchError("No Detail Data")
                data_extractor = self._imports["DataExtractor"](self.parameters)
                dummy_recorder = self.DummyRecorder()
                logger.debug(f"[Async Fetch Inner] DataExtractor created: {data_extractor}")
                data_extractor_run_result = data_extractor.run([video_data_raw], dummy_recorder, tiktok=False)
                logger.debug(f"[Async Fetch Inner] data_extractor.run returned type: {type(data_extractor_run_result)}")
                processed_data = None
                try: processed_data = await data_extractor_run_result
                except TypeError as te:
                    if "NoneType" in str(te): raise VideoFetchError(f"数据处理失败: {te}") from te
                    else: raise
                except Exception as await_exc: raise VideoFetchError(f"数据处理未知错误: {await_exc}") from await_exc
                if not processed_data: raise VideoFetchError("No Processed Data")
                return processed_data[0]
                # --- Placeholder End ---

            def _run_fetch_in_thread():
                try: return asyncio.run(_fetch_tiktok_video_async(trace_id, url))
                except Exception as thread_e: logger.error(f"[Celery TikTok Service Thread] Error: {thread_e}", exc_info=True, extra=log_extra); raise thread_e

            logger.debug("[Celery TikTok Service] 执行基础信息获取 (ThreadPool)...", extra=log_extra)
            with ThreadPoolExecutor(max_workers=1) as executor:
                future = executor.submit(_run_fetch_in_thread)
                try: media_data = future.result(timeout=self.timeout + 10)
                except TimeoutError: raise TikTokError(f"获取基础信息超时 (ThreadPool)")
                except Exception as e: raise TikTokError(f"获取基础信息失败: {e}") from e

            logger.debug("[Celery TikTok Service] 获取到基础信息", extra=log_extra)
            points_consumed = base_cost
            if media_data is None: raise TikTokError("未能获取基础信息")


            logger.info(f"[Celery TikTok Service] 处理成功: {url},media_data is{media_data}", extra=log_extra)
            return {"status": "success", "data": media_data, "points_consumed": points_consumed}

        # 保持错误处理不变
        except (TikTokError, InitializationError, AudioDownloadError, AudioTranscriptionError, VideoFetchError, UserFetchError) as e:
             error_msg = f"处理失败 ({type(e).__name__}): {str(e)}"
             logger.error(f"[Celery TikTok Service] {error_msg}", extra=log_extra, exc_info=False)
             return {"status": "failed", "error": error_msg, "points_consumed": 0}
        except Exception as e:
             error_msg = f"发生意外错误: {str(e)}"
             logger.error(f"[Celery TikTok Service] {error_msg}", exc_info=True, extra=log_extra)
             return {"status": "failed", "error": f"发生内部错误 ({trace_id})", "exception": str(e), "points_consumed": 0}
        finally:
             self.close_sync() # 确保调用清理

    

    def get_user_profile_by_url(
        self,
        user_url: str,
        log_extra: Dict[str, Any],
        retries: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        根据 TikTok/抖音用户主页 URL 获取详细的用户信息。

        参数：
            user_url: 用户主页的 URL（可以是分享链接或直接链接）。
            log_extra: 日志附加信息字典。
            retries: 底层操作的重试次数。

        返回：
            包含用户详细信息的字典。

        异常：
            UserFetchError: 如果无法获取用户信息或无法提取 sec_user_id。
            InitializationError: 如果服务未正确初始化。
        """
        if not self.parameters:
            raise InitializationError(
                "服务未正确初始化。请使用 'async with' 语句。"
            )

        _retries = self.max_retries if retries is None else retries
        logger.info(f"开始获取用户主页信息，URL: {user_url}", extra=log_extra)

        try:
            # 步骤 1: 解析 URL 以获取 sec_user_id
            link_extractor = self._imports["Extractor"](self.parameters) # 对应抖音
            extracted_sec_ids = link_extractor.run(user_url, type_="user")

            if not extracted_sec_ids or not isinstance(extracted_sec_ids, list) or len(extracted_sec_ids) == 0:
                logger.warning(f"无法从 URL 提取 sec_user_id: {user_url}", extra=log_extra)
                raise UserFetchError(f"未能在 URL 中找到 sec_user_id: {user_url}")

            sec_user_id = extracted_sec_ids[0]
            logger.debug(f"成功从 URL 提取 sec_user_id: {sec_user_id}，URL: {user_url}", extra=log_extra)

            # 步骤 2: 使用获取到的 sec_user_id 调用已有的 get_user_info 方法
            user_profile_data = self.get_user_info(sec_user_id, retries=_retries)
            
            logger.info(f"成功获取用户主页信息，sec_user_id: {sec_user_id}，URL: {user_url}", extra=log_extra)
            return user_profile_data

        except UserFetchError:
            logger.error(f"获取用户主页信息失败，URL: {user_url}（UserFetchError）", exc_info=True, extra=log_extra)
            raise
        except InitializationError:
            logger.error(f"获取用户主页信息时服务未初始化，URL: {user_url}", exc_info=True, extra=log_extra)
            raise
        except Exception as e:
            logger.error(f"获取用户主页信息发生未知错误，URL {user_url}，错误: {str(e)}", exc_info=True, extra=log_extra)
            raise UserFetchError(
                f"获取用户主页信息失败，URL {user_url}，错误: {str(e)}"
            ) from e


    def get_user_info(
        self, 
        sec_user_id: str, 
        retries: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        Get detailed information about a TikTok/Douyin user.
        
        Args:
            sec_user_id: TikTok/Douyin user's sec_user_id
            retries: Number of retry attempts (defaults to self.max_retries)
            
        Returns:
            Dictionary containing user details
            
        Raises:
            UserFetchError: If user information cannot be retrieved
        """
        if not self.parameters:
            raise InitializationError(
                "Service not properly initialized. Use 'async with' statement."
            )
        
        # Use class default if retries not specified
        retries = self.max_retries if retries is None else retries
        
        logger.info(f"Fetching user info for sec_user_id: {sec_user_id}")
        
        # Implement retry logic
        for attempt in range(retries + 1):
            try:
                # Get user details
                user = self._imports["User"](
                    self.parameters,
                    sec_user_id=sec_user_id
                )
                
                user_data = user.run()
                if not user_data:
                    logger.warning(f"Could not fetch details for user: {sec_user_id}")
                    raise UserFetchError(f"Failed to fetch details for user: {sec_user_id}")
                
                # Process the data
                data_extractor = self._imports["DataExtractor"](self.parameters)
                dummy_recorder = self.DummyRecorder()
                
                processed_data = data_extractor.run(
                    [user_data],
                    dummy_recorder,
                    type_="user"
                )
                
                if not processed_data:
                    logger.warning(f"Could not process data for user: {sec_user_id}")
                    raise UserFetchError(f"Failed to process data for user: {sec_user_id}")
                
                result = processed_data[0]
                logger.info(
                    f"Successfully fetched info for user: {result.get('nickname', 'Unknown')}"
                )
                
                return result
                
            except UserFetchError:
                # Re-raise specific errors without retrying
                raise
                
            except Exception as e:
                if attempt < retries:
                    wait_time = 2 ** attempt  # Exponential backoff
                    logger.warning(
                        f"Attempt {attempt+1}/{retries+1} failed: {str(e)}. "
                        f"Retrying in {wait_time}s..."
                    )
                    
                else:
                    logger.error(f"All {retries+1} attempts failed for user: {sec_user_id}")
                    raise UserFetchError(
                        f"Failed to get user info after {retries+1} attempts: {str(e)}"
                    ) from e


    
    def get_all_video_comments(
        self,
        video_url: str,
        fetch_replies: bool = False,
        max_comment_pages: Optional[int] = None,
        comments_per_page: int = 20, # 抖音/TikTok 单次请求通常返回的评论数量
        replies_per_comment_page: int = 3, # 如果 fetch_replies 为 True, TikTokDownloader 默认尝试获取的回复数
        platform: Optional[str] = None ,# 可选参数，用于明确指定平台 "douyin" 或 "tiktok"
        log_extra :  Dict[str, Any] = None
    ) -> List[Dict[str, Any]]:
        """
        获取指定抖音/TikTok视频URL的所有评论。

        参数:
            video_url (str): 视频的URL。
            fetch_replies (bool): 是否获取每条评论的回复。默认为 False。
            max_comment_pages (Optional[int]): 获取评论的最大页数（请求次数）。
                                             如果为 None，则会尝试获取所有可获取的评论页。
                                             注意：子模块内部有其自身的 `max_pages` 默认值。
            comments_per_page (int): 每次请求获取的评论数量。
            replies_per_comment_page (int): 如果 fetch_replies 为 True，每次请求评论的回复时，
                                            尝试获取的回复数量。
            platform (Optional[str]): 明确指定平台 "douyin" 或 "tiktok"。
                                      如果为 None，则尝试从URL推断。

        返回:
            List[Dict[str, Any]]: 评论列表，每个元素是一个包含评论信息的字典。

        可能抛出的异常:
            CommentError: 如果获取评论过程中发生特定于评论的错误。
            VideoFetchError: 如果无法从URL中提取视频ID。
            InitializationError: 如果服务未正确初始化。
            PlatformError: 如果无法确定平台或平台不受支持。
            TikTokError: 其他与TikTok/抖音服务相关的通用错误。
        """
        if not self.parameters:
            raise InitializationError("服务未正确初始化，请确保在 'async with' 语句中使用。")
        logger.info(f"开始为视频URL获取所有评论: {video_url}", extra=log_extra)

        try:
            current_platform = platform
            logger.debug(f"当前操作平台: {current_platform}", extra=log_extra)

            # 根据平台选择正确的链接提取器和评论接口类
            if current_platform == "tiktok":
                link_extractor_cls = self._imports.get("ExtractorTikTok")
                comment_interface_cls = self._imports.get("CommentTikTok")
                if not link_extractor_cls or not comment_interface_cls:
                    raise InitializationError("TikTok 的链接提取器 (ExtractorTikTok) 或评论接口 (CommentTikTok) 未能成功导入。")
            else: # 默认为抖音
                link_extractor_cls = self._imports.get("Extractor")
                comment_interface_cls = self._imports.get("Comment")
                if not link_extractor_cls or not comment_interface_cls:
                    raise InitializationError("抖音的链接提取器 (Extractor) 或评论接口 (Comment) 未能成功导入。")
            
            link_extractor = link_extractor_cls(self.parameters)
            video_ids = link_extractor.run(video_url, type_="detail")

            if not video_ids or not isinstance(video_ids, list) or not video_ids[0]:
                logger.warning(f"无法从URL中提取视频ID (detail_id): {video_url}", extra=log_extra)
                raise VideoFetchError(f"未能从URL {video_url} 中找到有效的视频ID。")

            detail_id = video_ids[0]
            logger.debug(f"从URL {video_url} 成功提取到视频ID (detail_id): {detail_id}", extra=log_extra)

            # --- 步骤 2: 实例化并运行评论获取器 ---
            # `Comment` 类内部的 `pages` 参数控制总的API请求次数。
            # 如果 `max_comment_pages` 未指定，则使用服务初始化时 `Parameter` 对象中定义的 `max_pages`。
            effective_max_pages = max_comment_pages if max_comment_pages is not None else self.parameters.max_pages
            
            # 根据平台选择使用的 cookie
            current_cookie = None
            if self.cookie: # 如果服务初始化时传入了cookie
                current_cookie = self.cookie
            elif current_platform == "tiktok":
                current_cookie = self.parameters.cookie_str_tiktok or self.parameters.cookie_dict_tiktok
            # else: # douyin
            #     current_cookie = self.parameters.cookie_str or self.parameters.cookie_dict
            
            if isinstance(current_cookie, dict): # Comment 类期望 string 类型的 cookie
                current_cookie_str = self._imports["Cookie"].cookie_dict_to_str_for_header(current_cookie) # 假设有此方法或类似
            else:
                current_cookie_str = current_cookie


            comment_fetcher = comment_interface_cls(
                self.parameters,
                cookie=current_cookie_str, # 传递合适的 cookie 字符串
                item_id=detail_id,
                pages=effective_max_pages,
                count=comments_per_page,
                reply=fetch_replies,
                count_reply=replies_per_comment_page 
            )

            logger.debug(f"开始请求视频ID {detail_id} 的评论数据...", extra=log_extra)
            raw_comments_data = comment_fetcher.run() # error_text 参数可用于自定义无评论时的日志信息

            if not raw_comments_data: # 可能返回空列表或None
                logger.info(f"视频ID {detail_id} 没有评论，或未能成功获取评论。", extra=log_extra)
                return [] 

            logger.debug(f"成功获取视频ID {detail_id} 的原始评论数据共 {len(raw_comments_data)} 条。", extra=log_extra)

            # --- 步骤 3: 处理和格式化评论数据 ---
            data_extractor = self._imports["DataExtractor"](self.parameters)
            dummy_recorder = self.DummyRecorder() # 使用虚拟记录器，因为我们只提取数据，不保存到子模块的存储

            processed_comments = data_extractor.run(
                raw_comments_data,
                dummy_recorder,
                type_="comment", # 告知提取器处理的是评论数据
                tiktok=(current_platform == "tiktok") # 传递平台信息给提取器
            )

            logger.info(f"成功获取并处理了 {len(processed_comments)} 条评论 (视频ID: {detail_id}, URL: {video_url})。", extra=log_extra)
            return processed_comments

        except VideoFetchError as vfe:
            logger.error(f"为评论提取视频ID失败 (URL: {video_url}): {str(vfe)}", exc_info=True, extra=log_extra)
            raise # 此类错误通常表示输入URL有问题，直接抛出
        # except PlatformError as pe:
        #     logger.error(f"平台处理错误 (URL: {video_url}): {str(pe)}", exc_info=True, extra=log_extra)
        #     raise
        except InitializationError as ie:
            logger.error(f"服务初始化错误，无法获取评论 (URL: {video_url}): {str(ie)}", exc_info=True, extra=log_extra)
            raise
        # except TikTokError as te: # 捕获子模块或服务定义的其他 TikTokError
        #     logger.error(f"获取评论时发生TikTok/抖音服务相关错误 (URL: {video_url}): {str(te)}", exc_info=True, extra=log_extra)
        #     raise CommentError(f"获取视频评论失败: {str(te)}") from te
        except Exception as e:
            # 对于未知错误，记录详细信息并包装成 CommentError
            logger.error(f"获取视频评论时发生意外错误 (URL: {video_url}): {str(e)}", exc_info=True, extra=log_extra)
            # raise CommentError(f"获取视频 {video_url} 的评论时发生意外错误: {str(e)}") from e
            raise

    

    def get_all_user_posts(
        self,
        user_url_or_sec_id: str,
        max_video_pages: Optional[int] = None, # 获取作品列表的最大页数，None表示获取所有
        videos_per_page: int = 18, # 抖音 Account 接口默认 count 为 18
                                     # TikTok AccountTikTok 接口默认 count 为 35
        platform: Optional[str] = None, # 可选参数，用于明确指定平台 "douyin" 或 "tiktok"
        # 以下参数通常由 Account 类内部默认处理，或可从 Parameter 对象获取，但也可在此处覆盖
        # earliest: Optional[str] = None, # 可选：作品最早发布日期 (格式 "YYYY/MM/DD" 或相对天数)
        # latest: Optional[str] = None,   # 可选：作品最晚发布日期
        log_extra :  Dict[str, Any] = None
    ) -> List[Dict[str, Any]]:
        """
        获取指定抖音/TikTok用户发布的所有作品（视频/图集）。

        参数:
            user_url_or_sec_id (str): 用户的主页URL或其 sec_user_id。
            max_video_pages (Optional[int]): 获取作品列表的最大页数（API请求次数）。
                                           如果为 None，则尝试获取所有页。
            videos_per_page (int): 每次API请求获取的作品数量。
                                   注意：抖音和TikTok的默认值和上限可能不同。
            platform (Optional[str]): 明确指定平台 "douyin" 或 "tiktok"。
                                      如果为 None，且输入的是URL，则尝试从URL推断。
                                      如果输入的是 sec_user_id，则必须提供 platform。
            # earliest (Optional[str]): 筛选作品的最早发布日期。
            # latest (Optional[str]): 筛选作品的最晚发布日期。

        返回:
            List[Dict[str, Any]]: 作品列表，每个元素是一个包含作品信息的字典。

        可能抛出的异常:
            UserFetchError: 如果无法获取用户信息或作品。
            VideoFetchError: 如果从URL提取sec_user_id失败。
            InitializationError: 如果服务未正确初始化。
            PlatformError: 如果无法确定平台或平台不受支持。
            TikTokError: 其他与TikTok/抖音服务相关的通用错误。
        """
        if not self.parameters:
            raise InitializationError("服务未正确初始化，请确保在 'async with' 语句中使用。")

        logger.info(f"开始获取用户发布的所有作品: {user_url_or_sec_id}", extra=log_extra)

        sec_user_id_to_use = None
        current_platform = None

        try:
            # --- 步骤 1: 确定平台和 sec_user_id ---
            is_likely_url = "http://" in user_url_or_sec_id.lower() or "https://" in user_url_or_sec_id.lower()

            if platform is None:
                if is_likely_url:
                    if "tiktok.com" in user_url_or_sec_id.lower():
                        current_platform = "tiktok"
                    elif "douyin.com" in user_url_or_sec_id.lower():
                        current_platform = "douyin"
                    else:
                        logger.warning(f"无法从URL推断平台: {user_url_or_sec_id}。请通过 'platform' 参数明确指定。", extra=log_extra)
                        raise PlatformError(f"无法从URL {user_url_or_sec_id} 自动判断平台。")
                else: # 如果不是URL且未指定平台，则无法继续
                    logger.error(f"输入的是 sec_user_id ({user_url_or_sec_id}) 但未指定 'platform' 参数。", extra=log_extra)
                    raise PlatformError("当直接提供 sec_user_id 时，必须指定 'platform' 参数。")
            else:
                current_platform = platform.lower()
                if current_platform not in ["douyin", "tiktok"]:
                    raise PlatformError(f"不支持的平台: {platform}。请使用 'douyin' 或 'tiktok'。")
            
            log_extra["platform"] = current_platform # 更新日志上下文
            logger.debug(f"当前操作平台: {current_platform}", extra=log_extra)

            if is_likely_url:
                link_extractor_cls = self._imports.get("ExtractorTikTok") if current_platform == "tiktok" else self._imports.get("Extractor")
                if not link_extractor_cls:
                    raise InitializationError(f"{current_platform.capitalize()} 的链接提取器未能成功导入。")
                
                link_extractor = link_extractor_cls(self.parameters)
                extracted_ids = link_extractor.run(user_url_or_sec_id, type_="user")
                if not extracted_ids or not isinstance(extracted_ids, list) or not extracted_ids[0]:
                    logger.warning(f"无法从URL中提取 sec_user_id: {user_url_or_sec_id}", extra=log_extra)
                    raise UserFetchError(f"未能从URL {user_url_or_sec_id} 中找到有效的 sec_user_id。")
                sec_user_id_to_use = extracted_ids[0]
                logger.debug(f"从URL {user_url_or_sec_id} 成功提取到 sec_user_id: {sec_user_id_to_use}", extra=log_extra)
            else: #直接是sec_id
                sec_user_id_to_use = user_url_or_sec_id
                logger.debug(f"直接使用提供的 sec_user_id: {sec_user_id_to_use}", extra=log_extra)


            # --- 步骤 2: 实例化并运行账号作品获取器 ---
            account_interface_cls = self._imports.get("AccountTikTok") if current_platform == "tiktok" else self._imports.get("Account")
            if not account_interface_cls:
                raise InitializationError(f"{current_platform.capitalize()} 的账号作品接口 (Account/AccountTikTok) 未能成功导入。")

            effective_max_pages = max_video_pages if max_video_pages is not None else self.parameters.max_pages
            
            # 根据平台选择合适的 cookie 和代理
            current_cookie = None
            proxy_to_use = None
            if self.cookie: # 如果服务初始化时传入了cookie
                current_cookie = self.cookie
            elif current_platform == "tiktok":
                current_cookie = self.parameters.cookie_str_tiktok or self.parameters.cookie_dict_tiktok
                proxy_to_use = self.parameters.proxy_tiktok
            else: # douyin
                current_cookie = self.parameters.cookie_str or self.parameters.cookie_dict
                proxy_to_use = self.parameters.proxy

            if isinstance(current_cookie, dict):
                 # 确保能正确调用到 cookie 字典转字符串的逻辑
                format_cookie_dict_to_str_func_name = "cookie_dict_to_str" # 根据您 Cookie 类中实际方法名或工具函数名调整
                cookie_tools = self._imports.get("Cookie") # 或者您存储工具函数的特定键
                format_method = getattr(cookie_tools, format_cookie_dict_to_str_func_name, None)
                if format_method:
                    cookie_to_use_str = format_method(current_cookie)
                else:
                    cookie_to_use_str = "; ".join([f"{k}={v}" for k, v in current_cookie.items()])
                    logger.warning("未能找到 cookie_dict_to_str 方法，尝试了简单拼接 cookie 字典。", extra=log_extra)
            else:
                cookie_to_use_str = current_cookie

            account_fetcher = account_interface_cls(
                params=self.parameters, # 注意：Account类构造函数的第一个参数是 Parameter 对象
                cookie=cookie_to_use_str,
                proxy=proxy_to_use,
                sec_user_id=sec_user_id_to_use,
                tab="post", # 明确指定获取已发布作品
                pages=effective_max_pages,
                count=videos_per_page # AccountTikTok 和 Account 类都接受 count 参数
                # earliest=earliest, # 如果需要日期筛选，传递这些参数
                # latest=latest,
            )

            logger.debug(f"开始请求用户 {sec_user_id_to_use} 的作品列表...", extra=log_extra)
            # Account().run() 返回 (作品列表, earliest_date, latest_date)
            # 我们只需要作品列表
            raw_posts_data, _, _ = account_fetcher.run(single_page=(effective_max_pages == 1))


            if not raw_posts_data: # 可能返回空列表或None
                logger.info(f"用户 {sec_user_id_to_use} 没有已发布的作品，或未能成功获取作品列表。", extra=log_extra)
                return [] 

            logger.debug(f"成功获取用户 {sec_user_id_to_use} 的原始作品数据共 {len(raw_posts_data)} 条。", extra=log_extra)

            # --- 步骤 3: 处理和格式化作品数据 ---
            data_extractor = self._imports["DataExtractor"](self.parameters)
            dummy_recorder = self.DummyRecorder()

            # Extractor.run(type_="batch") 用于处理批量作品数据
            processed_posts = data_extractor.run(
                raw_posts_data,
                dummy_recorder,
                type_="batch", # 告知提取器处理的是批量作品数据
                tiktok=(current_platform == "tiktok"), # 传递平台信息
                name=sec_user_id_to_use, # 可选，用于内部日志或标记，通常是用户名，这里用sec_id代替
                mark=sec_user_id_to_use, # 同上
                earliest=None, # 如果不需要日期筛选，传递None
                latest=None,   # 如果不需要日期筛选，传递None
                same=True # 假设所有作品来自同一作者
            )

            logger.info(f"成功获取并处理了 {len(processed_posts)} 个作品 (用户: {sec_user_id_to_use})。", extra=log_extra)
            return processed_posts

        except (UserFetchError, VideoFetchError) as specific_error: # VideoFetchError 用于 URL 解析 sec_id 失败
            logger.error(f"获取用户作品失败 (用户输入: {user_url_or_sec_id}): {str(specific_error)}", exc_info=True, extra=log_extra)
            raise
        # except PlatformError as pe:
        #     logger.error(f"获取用户作品中的平台处理错误 (用户输入: {user_url_or_sec_id}): {str(pe)}", exc_info=True, extra=log_extra)
        #     raise
        except InitializationError as ie:
            logger.error(f"服务初始化错误，无法获取用户作品 (用户输入: {user_url_or_sec_id}): {str(ie)}", exc_info=True, extra=log_extra)
            raise
        except TikTokError as te:
            logger.error(f"获取用户作品时发生TikTok/抖音服务相关错误 (用户输入: {user_url_or_sec_id}): {str(te)}", exc_info=True, extra=log_extra)
            raise UserFetchError(f"获取用户作品失败: {str(te)}") from te
        except Exception as e:
            logger.error(f"获取用户作品时发生意外错误 (用户输入: {user_url_or_sec_id}): {str(e)}", exc_info=True, extra=log_extra)
            raise UserFetchError(f"获取用户 {user_url_or_sec_id} 的作品时发生意外错误: {str(e)}") from e



    def search_douyin_content_sync(
        self,
        keyword: str,
        channel: int = 0,  # 0:综合, 1:视频, 2:用户, 3:直播
        pages: int = 1,    # 希望获取的页数
        search_specific_params: Optional[Dict[str, Any]] = None, # 特定搜索类型的参数，如 sort_type, publish_time 等
        user_id_for_points: Optional[str] = None, # 用于日志和计费
        trace_id: Optional[str] = None, # 用于日志追踪
        root_trace_key: Optional[str] = None # 用于日志追踪
    ) -> Dict[str, Any]:
        """
        [同步执行] 根据关键字搜索抖音内容。
        channel: 0=综合, 1=视频, 2=用户, 3=直播
        search_specific_params: 特定于频道的可选参数字典 (例如: sort_type, publish_time)
        """
        log_extra = {
            "request_id": trace_id, 
            "user_id": user_id_for_points, 
            "keyword": keyword,
            "channel": channel,
            "root_trace_key": root_trace_key
        }
        logger.info(f"[Celery TikTok Service] 开始搜索: keyword='{keyword}', channel={channel}", extra=log_extra)

        if not self.parameters:
            logger.error("[Celery TikTok Service] 服务未初始化 (parameters is None)!", extra=log_extra)
            raise InitializationError("服务未初始化 (parameters is None)")
        
        if search_specific_params is None:
            search_specific_params = {}

        try:
            # 准备搜索模型
            search_model_class = None
            if channel == 0: # 综合搜索
                search_model_class = self._imports["GeneralSearch"]
                # 默认参数，可以从 search_specific_params 覆盖
                model_params = {
                    "keyword": keyword, "pages": pages,
                    "sort_type": search_specific_params.get("sort_type", 0),
                    "publish_time": search_specific_params.get("publish_time", 0),
                    "duration": search_specific_params.get("duration", 0),
                    "search_range": search_specific_params.get("search_range", 0),
                    "content_type": search_specific_params.get("content_type", 0),
                }
            elif channel == 1: # 视频搜索
                search_model_class = self._imports["VideoSearch"]
                model_params = {
                    "keyword": keyword, "pages": pages,
                    "sort_type": search_specific_params.get("sort_type", 0),
                    "publish_time": search_specific_params.get("publish_time", 0),
                    "duration": search_specific_params.get("duration", 0),
                    "search_range": search_specific_params.get("search_range", 0),
                }
            elif channel == 2: # 用户搜索
                search_model_class = self._imports["UserSearch"]
                model_params = {
                    "keyword": keyword, "pages": pages,
                    "douyin_user_fans": search_specific_params.get("douyin_user_fans", 0),
                    "douyin_user_type": search_specific_params.get("douyin_user_type", 0),
                }
            elif channel == 3: # 直播搜索
                search_model_class = self._imports["LiveSearch"]
                model_params = {"keyword": keyword, "pages": pages}
            else:
                raise TikTokError(f"不支持的搜索频道: {channel}")

            # 验证并创建搜索参数模型实例
            # Pydantic 模型会自动验证参数
            try:
                current_search_model = search_model_class(**model_params)
            except Exception as model_exc: # 更通用的Pydantic验证错误捕获
                 logger.error(f"搜索参数模型验证失败: {model_exc}", extra=log_extra, exc_info=True)
                 raise TikTokError(f"搜索参数无效: {model_exc}") from model_exc


            async def _fetch_search_results_async():
                logger.debug("[Celery TikTok Service] ---> Entering _fetch_search_results_async (in thread) <---", extra=log_extra)
                
                search_instance = self._imports["Search"](
                    self.parameters, # Parameter 实例
                    keyword=current_search_model.keyword,
                    channel=current_search_model.channel,
                    pages=current_search_model.pages,
                    # 根据模型的字段传递参数
                    **{k: v for k, v in current_search_model.model_dump().items() if k not in ['keyword', 'channel', 'pages']}
                )
                logger.debug(f"[Async Search Inner] Search instance created: {search_instance}")
                
                raw_search_data = await search_instance.run()
                if not raw_search_data:
                    logger.info("[Async Search Inner] 搜索API未返回数据", extra=log_extra)
                    return [] # 返回空列表表示无结果

                data_extractor = self._imports["DataExtractor"](self.parameters)
                dummy_recorder = self.DummyRecorder() 
                
                # DataExtractor的tab参数对应Search类中的channel (int类型)
                processed_data = await data_extractor.run(
                    raw_search_data, 
                    dummy_recorder, 
                    type_="search", 
                    tiktok=False, # 搜索功能目前是针对抖音的
                    tab=current_search_model.channel 
                )
                logger.debug(f"[Async Search Inner] DataExtractor processed {len(processed_data)} items.", extra=log_extra)
                return processed_data

            def _run_fetch_in_thread():
                try:
                    # 使用已有的 run_async_in_new_loop 辅助函数
                    return run_async_in_new_loop(_fetch_search_results_async())
                except Exception as thread_e:
                    logger.error(f"[Celery TikTok Service Search Thread] Error: {thread_e}", exc_info=True, extra=log_extra)
                    raise thread_e
            
            logger.debug("[Celery TikTok Service] 执行搜索 (ThreadPool)...", extra=log_extra)
            search_results_data = None
            with ThreadPoolExecutor(max_workers=1) as executor:
                future = executor.submit(_run_fetch_in_thread)
                try:
                    search_results_data = future.result(timeout=self.timeout + 20) # 搜索可能耗时更长
                except TimeoutError:
                    logger.error(f"搜索超时: {keyword}", extra=log_extra)
                    raise TikTokError(f"搜索超时 (ThreadPool): {keyword}")
                except Exception as e:
                    logger.error(f"搜索失败: {keyword}, Error: {e}", extra=log_extra, exc_info=True)
                    raise TikTokError(f"搜索失败 (ThreadPool): {e}") from e

            logger.info(f"[Celery TikTok Service] 搜索处理成功: keyword='{keyword}', 结果数量: {len(search_results_data) if search_results_data else 0}", extra=log_extra)
            # 假设搜索也消耗点数，这里简单设置为一个固定值，你可以根据需要调整
            points_consumed = 15 
            return {"status": "success", "data": search_results_data, "points_consumed": points_consumed}

        except (TikTokError, InitializationError) as e:
            error_msg = f"处理失败 ({type(e).__name__}): {str(e)}"
            logger.error(f"[Celery TikTok Service] {error_msg}", extra=log_extra, exc_info=False)
            return {"status": "failed", "error": error_msg, "points_consumed": 0}
        except Exception as e:
            error_msg = f"发生意外错误: {str(e)}"
            logger.error(f"[Celery TikTok Service] {error_msg}", exc_info=True, extra=log_extra)
            return {"status": "failed", "error": f"发生内部错误 ({trace_id})", "exception": str(e), "points_consumed": 0}
        finally:
            self.close_sync() # 确保调用清理

    # 你可以将原来的 search_note_by_kword_logic 替换或调用此新方法
    def search_note_by_kword_logic(self, keyword: str, log_extra: Dict[str, Any], **kwargs):
        # 示例：调用新的搜索方法，这里默认进行综合搜索
        # 你需要从 log_extra 或其他地方获取 trace_id, user_id_for_points 等参数
        trace_id = log_extra.get("request_id")
        user_id_for_points = log_extra.get("user_id")
        root_trace_key = log_extra.get("root_trace_key")

        # 提取特定于搜索的参数，如果它们被传递到 kwargs 中
        channel = kwargs.get("channel", 0) # 默认为综合搜索
        pages = kwargs.get("pages", 1)
        search_specific_params = kwargs.get("search_specific_params", {})
        
        return self.search_douyin_content_sync(
            keyword=keyword,
            channel=channel,
            pages=pages,
            search_specific_params=search_specific_params,
            user_id_for_points=user_id_for_points,
            trace_id=trace_id,
            root_trace_key=root_trace_key
        )